import { type Ref, ref, type ShallowRef, shallowRef, watch, type WatchOptions } from "vue";
import { getLocalJson, getSessionJson, setLocalJson, setSessionJson } from "@/framework/util/storage";

export interface LocalWatchOptions extends WatchOptions {
  onChange?: (value: any) => void;
}

export function useLocalRef<T>(key: string, valueFactory: () => T, opts?: LocalWatchOptions): Ref<T>;
export function useLocalRef<T>(key: string, value: T, opts?: LocalWatchOptions): Ref<T>;
export function useLocalRef<T = any>(key: string): Ref<T | undefined>;
export function useLocalRef<T>(key: string, value?: T | (() => T), opts?: LocalWatchOptions) {
  const state = ref(getLocalJson(key) ?? (typeof value === "function" ? (value as Function)() : value));
  const watchOpts = { ...opts };
  watchOpts.onChange = undefined;
  watch(
    state,
    v => {
      setLocalJson(key, v);
      opts?.onChange?.(v);
    },
    watchOpts,
  );
  return state;
}

export function useLocalShallowRef<T>(key: string, valueFactory: () => T): ShallowRef<T>;
export function useLocalShallowRef<T>(key: string, value: T): ShallowRef<T>;
export function useLocalShallowRef<T = any>(key: string): ShallowRef<T | undefined>;
export function useLocalShallowRef<T>(key: string, value?: T | (() => T)) {
  const state = shallowRef(getLocalJson(key) ?? (typeof value === "function" ? (value as Function)() : value));
  watch(state, v => setLocalJson(key, v));
  return state;
}

// session

export function useSessionRef<T>(key: string, valueFactory: () => T): Ref<T>;
export function useSessionRef<T>(key: string, value: T): Ref<T>;
export function useSessionRef<T = any>(key: string): Ref<T | undefined>;
export function useSessionRef<T>(key: string, value?: T | (() => T)) {
  const state = ref(getSessionJson(key) ?? (typeof value === "function" ? (value as Function)() : value));
  watch(state, v => setSessionJson(key, v));
  return state;
}

export function useSessionShallowRef<T>(key: string, valueFactory: () => T): ShallowRef<T>;
export function useSessionShallowRef<T>(key: string, value: T): ShallowRef<T>;
export function useSessionShallowRef<T = any>(key: string): ShallowRef<T | undefined>;
export function useSessionShallowRef<T>(key: string, value?: T | (() => T)) {
  const state = shallowRef(getSessionJson(key) ?? (typeof value === "function" ? (value as Function)() : value));
  watch(state, v => setSessionJson(key, v));
  return state;
}
